#include <stdio.h>
#include <stdlib.h>
#define TTY_PATH            "/dev/tty"
#define STTY_US             "stty raw -echo -F "
#define STTY_DEF            "stty -raw echo -F "

static int get_char()	//能实现非阻塞IO, 如果没有按下按键，程序继续往下走
{
    fd_set rfds;
    struct timeval tv;
    int ch = 0;

    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    tv.tv_sec = 0;
    tv.tv_usec = 10; //设置等待超时时间

    //检测键盘是否有输入
    if (select(1, &rfds, NULL, NULL, &tv) > 0)
    {
        ch = getchar(); 
    }
	
    return ch;		//返回按键值
}
struct point_pos		//此结构体保存一个点的坐标
{
	int x;
	int y;
};
struct point_pos bean;		//豆子
int score = 0;				//分数
int level = 1;				//等级
int speed = 10;				//速度， 值越大，运行越慢
int block[20][20] = { 0 };	//背景，1   墙，  0 空格
void gotoxy(int x, int y)
{
	printf("\033[%d;%dH", y + 1, x + 1);
}
int color[7] = {31, 32, 33, 34, 35, 36, 37};
struct point_pos snake[100] = { 0 };		//蛇身
int snake_len = 3;			//蛇的长度，最长100个点
void new_snake()		//出一个蛇, 有两个点， 一个蛇头，一个蛇身
{
	snake[0].x = 9;
	snake[0].y = 9;
	snake[1].x = 9;
	snake[1].y = 10;
	snake[2].x = 9;
	snake[2].y = 11;
}
int snake_color = 1;
int snake_head_color = 2;
void draw_snake()		//画一个蛇
{
	int i;
	gotoxy(snake[0].x * 2, snake[0].y);	//
	printf("\033[1;%dm★\033[0m", color[snake_head_color]);	//先画蛇头
	for(i = 1; i < snake_len; i++)
	{
		gotoxy(snake[i].x * 2, snake[i].y);	//
		printf("\033[1;%dm◆\033[0m", color[snake_color]);	//再画蛇头
	}
}
int judge_bean()		//判断豆子是否有效，有效返回0， 无效返回1
{
	int i;
	for(i = 0; i < snake_len; i++)
	{
		if(bean.x == snake[i].x && bean.y == snake[i].y)
		{
			return 1;
		}
	}
	return 0;		//说明，豆子没在蛇身上
}
void new_bean()			//出一个新豆子, 只要将x, y 赋值即可
{						//如果豆子出在蛇身上，重出一个豆子
	do					//先执行， 再判断
	{
		bean.x = rand() % 16 + 2;	// 2 - 17
		bean.y = rand() % 16 + 2;
	}while(judge_bean() == 1);		//检查出的豆子是否在蛇身上
}

int bean_color = 0;
void draw_bean()		//画一个豆子
{
	gotoxy(bean.x * 2, bean.y);		//跳转到坐标点
	printf("\033[1;%dm●\033[0m", color[bean_color]);	//打印出一个豆子
}

void init_block()		//初始化墙，四边是1
{
	int i;
	for(i = 0; i < 20; i++)
	{
		block[0][i] = 1;	//让数组中相应位置是墙，后面会描画
		block[19][i] = 1;
		block[i][0] = 1;
		block[i][19] = 1;
	}
}
void draw_block()		//将墙画出来
{
	int i, j;
	gotoxy(0, 0);
	for(i = 0; i < 20; i++)
	{
		for(j = 0; j < 20; j++)
		{
			if(block[i][j] == 1)
				printf("\033[1;34m□ \033[0m");
			else
				printf("  ");		//能实现擦除功能
		}
		printf("\r\n");
	}
}
//蛇吃豆子 
void add_bean_to_snake()
{
	int i;
	for(i = snake_len - 1; i >= 0; i--)	//从蛇头开始，每个赋值给后面
		snake[i + 1] = snake[i];
	snake_len++;
	snake[0].x = bean.x;		//豆子坐标赋值给蛇头
	snake[0].y = bean.y;
	new_bean();
	score += 100;				//吃到一个豆子加100分
	if(score % 500 == 0)
	{
		level++;
		speed--;
		if(speed < 2)
			speed = 2;
	}
	bean_color++;
	if(bean_color > 6)
		bean_color = 0;
	snake_color++;
	if(snake_color > 6)
		snake_color = 0;
	snake_head_color++;
	if(snake_head_color > 6)
		snake_head_color = 0;		
}

#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3
int direction = UP;	//0----> up
void snake_move_right()		//蛇向右走
{	//蛇头 x+1, y不变   
	int i;
	//判断是否蛇头碰到了豆子
	if(snake[0].x + 1 == bean.x && snake[0].y == bean.y)
	{
		add_bean_to_snake();
		return;
	}
	for(i = snake_len - 1; i > 0; i--)	//除了蛇头，每个节点前移
	{
		snake[i] = snake[i - 1];
	}
	snake[0].x++;		//y不变

}

void snake_move_left()		//蛇向左走
{	//蛇头 x+1, y不变   
	int i;
	//判断是否蛇头碰到了豆子
	if(snake[0].x - 1 == bean.x && snake[0].y == bean.y)
	{
		add_bean_to_snake();
		return;
	}	
	for(i = snake_len - 1; i > 0; i--)	//除了蛇头，每个节点前移
	{
		snake[i] = snake[i - 1];
	}
	snake[0].x--;		//y不变
}

void snake_move_up()		//蛇向上走
{	//蛇头 x+1, y不变   
	int i;
	//判断是否蛇头碰到了豆子
	if(snake[0].y - 1 == bean.y && snake[0].x == bean.x)
	{
		add_bean_to_snake();
		return;
	}
	for(i = snake_len - 1; i > 0; i--)	//除了蛇头，每个节点前移
	{
		snake[i] = snake[i - 1];
	}
	snake[0].y--;		//y不变
}

void snake_move_down()		//蛇向下走
{	//蛇头 x+1, y不变   
	int i;
	//判断是否蛇头碰到了豆子
	if(snake[0].y + 1 == bean.y && snake[0].x == bean.x)
	{
		add_bean_to_snake();
		return;
	}
	for(i = snake_len - 1; i > 0; i--)	//除了蛇头，每个节点前移
	{
		snake[i] = snake[i - 1];
	}
	snake[0].y++;		//y不变
}

void snake_move()		//因为如果没按按键，蛇也应该自己走
{
	if(direction == RIGHT)
	{
		snake_move_right();
	}
	else if(direction == LEFT)	
	{
		snake_move_left();
	}
	else if(direction == UP)
	{
		snake_move_up();
	}
	else if(direction == DOWN)
	{
		snake_move_down();
	}
}
int judge_game_over()		//判断游戏是否结束
{
	int i;
	if(snake[0].x > 18 || snake[0].x <= 0 || snake[0].y > 18 || snake[0].y <= 0)
	{
		return 1;		//返回1，代表游戏结束
	}
	for(i = 1; i < snake_len; i++)
	{
		if(snake[0].x == snake[i].x && snake[0].y == snake[i].y)	//撞到自身
		{
			return 1;
		}
	}
	return 0;			//返回0，表示游戏没结束
}
int main()
{
	int i;
	char ch;
	system("clear");
	system(STTY_US TTY_PATH);	//不用理解
	srand(time(0));		//产生随机种子
	init_block();
	new_bean();	
	new_snake();
	draw_block();		//清除整个屏幕
	gotoxy(42, 4);
	printf("space: start");
	gotoxy(42, 5);
	printf("space: pause");
	fflush(stdout);	
	ch = 1;
	while(ch != ' ')
	{
		ch = get_char();
	}
	while(1)
	{
		draw_block();		//清除整个屏幕
		draw_bean();
		draw_snake();
		gotoxy(42, 0);
		printf("score: %d    ", score);
		gotoxy(42, 1);
		printf("level: %d    ", level);
		gotoxy(42, 2);
		printf("speed: %d    ", 11 - speed);
		fflush(stdout);	
		if(judge_game_over() == 1)
		{
			gotoxy(8, 9);
			printf("-------game over------\n");
			system(STTY_DEF TTY_PATH);	//恢复非阻塞IO设置  //不用理解
			return 0;			//程序结束			
		}		
		fflush(stdout);		//刷新屏幕
		for(i = 0; i < speed; i++)
		{
			ch = get_char();	//换成get_char 非阻塞IO
			if(ch == ' ')		//暂停
			{
				ch = 1;
				while(ch != ' ')
				{
					ch = get_char();
				}
			}
			else if(ch == 'd' && direction != LEFT)
			{
				direction = RIGHT;
				break;
			}
			else if(ch == 'a' && direction != RIGHT)
			{
				direction = LEFT;
				break;
			}
			else if(ch == 'w' && direction != DOWN)
			{
				direction = UP;
				break;
			}
			else if(ch == 's' && direction != UP)
			{
				direction = DOWN;
				break;
			}
			else if(ch == 3)	//ctrl + c
			{
				system(STTY_DEF TTY_PATH);	//恢复非阻塞IO设置  //不用理解
				return 0;			//程序结束
			}
			usleep(50000);		//50ms
		}
		snake_move();
	}
}

